Skip to content

πŸ›‘οΈ Sentinel: [MEDIUM] Fix insecure file permissions in backup script#38

Open
kidchenko wants to merge 1 commit intomainfrom
security/backup-permissions-umask-9275972573300568072
Open

πŸ›‘οΈ Sentinel: [MEDIUM] Fix insecure file permissions in backup script#38
kidchenko wants to merge 1 commit intomainfrom
security/backup-permissions-umask-9275972573300568072

Conversation

@kidchenko
Copy link
Owner

@kidchenko kidchenko commented Feb 22, 2026

🚨 Severity: MEDIUM
πŸ’‘ Vulnerability: The tools/backup-projects.sh script created project backup zip files with default system permissions (often 644/664), making them readable by other users on the system.
🎯 Impact: Sensitive project files could be exposed to unauthorized users.
πŸ”§ Fix: Added umask 077 to the beginning of tools/backup-projects.sh to ensure all created files and directories are private (readable/writable only by the owner).
βœ… Verification: Added a regression test tests/verify_backup_permissions.sh which confirms that created backup files have 600 permissions.


PR created automatically by Jules for task 9275972573300568072 started by @kidchenko

Summary by CodeRabbit

  • Bug Fixes

    • Fixed a security vulnerability where backup archives were created with overly permissive file permissions, allowing other users to read sensitive data.
  • Tests

    • Added verification test to ensure backup files are created with restricted file permissions.
  • Documentation

    • Added security bulletin documenting the backup file permission vulnerability and remediation.

…eadable backups

- Adds `umask 077` to `tools/backup-projects.sh` to ensure generated zip files have 600 permissions.
- Adds `tests/verify_backup_permissions.sh` as a regression test.
- Adds security learning to `.jules/sentinel.md`.

Co-authored-by: kidchenko <5432753+kidchenko@users.noreply.github.com>
@google-labs-jules
Copy link
Contributor

πŸ‘‹ Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a πŸ‘€ emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

πŸ“ Walkthrough

Walkthrough

This PR addresses a backup file permission vulnerability by documenting the security issue in a dated bulletin, adding a test script to verify strict backup permissions, and enforcing restricted file permissions in the backup script via umask configuration.

Changes

Cohort / File(s) Summary
Security Documentation
.jules/sentinel.md
New security bulletin documenting a vulnerability where backup archives are created with overly permissive default permissions, allowing unauthorized read access. Prescribes using umask 077 to restrict access.
Backup Script Fix
tools/backup-projects.sh
Added umask 077 directive to enforce strict file and directory permissions (owner read/write only) for all created backup artifacts.
Permission Verification Test
tests/verify_backup_permissions.sh
New test script that validates backup files are created with restricted permissions (600), including setup, execution, permission verification, and cleanup steps with error handling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A backup once loose, now secure and tight,
With umask 077, permissions done right,
Tests verify safety, no secrets exposed,
Our files are private, our worries are closed! πŸ”

πŸš₯ Pre-merge checks | βœ… 3
βœ… Passed checks (3 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The title clearly identifies the main security fix: adding umask to ensure proper file permissions in the backup script, which aligns with all file changes in the PR.
Docstring Coverage βœ… Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • πŸ“ Generate docstrings (stacked PR)
  • πŸ“ Generate docstrings (commit on current branch)
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch security/backup-permissions-umask-9275972573300568072

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
tests/verify_backup_permissions.sh (1)

19-19: /tmp/backup_output.log leaks backup output and is never cleaned up.

The file is world-readable (the test script has no umask), and it persists across runs. Consider using mktemp and adding it to the cleanup trap.

♻️ Proposed fix
+BACKUP_LOG=$(mktemp)
+
 # Run backup script
-if ! bash tools/backup-projects.sh backup --verbose > /tmp/backup_output.log 2>&1; then
+if ! bash tools/backup-projects.sh backup --verbose > "$BACKUP_LOG" 2>&1; then
     echo "Backup failed. Output:"
-    cat /tmp/backup_output.log
+    cat "$BACKUP_LOG"
     exit 1
 fi

Then add "$BACKUP_LOG" to the cleanup function from the trap fix above:

 _cleanup() {
   rm -f "$PROJECT_DIR/secret.txt"
+  rm -f "$BACKUP_LOG"
   [[ "$_project_dir_existed" == false ]] && rm -rf "$PROJECT_DIR"
 }
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/verify_backup_permissions.sh` at line 19, Replace the hard-coded
/tmp/backup_output.log with a secure temporary file created via mktemp and store
its path in a BACKUP_LOG variable, ensure the file is created with restrictive
permissions (or set a safe umask before creation), use "$BACKUP_LOG" when
invoking bash tools/backup-projects.sh, and add "$BACKUP_LOG" to the existing
cleanup function invoked by the trap so the temp file is removed on exit/failure
(refer to BACKUP_LOG, the cleanup function, and the trap setup to locate where
to modify).
πŸ€– Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.jules/sentinel.md:
- Around line 1-4: Change the top-level heading to a single `#` and ensure it's
surrounded by a blank line, and wrap the description lines to <=80 characters to
satisfy MD041, MD022 and MD013; specifically edit .jules/sentinel.md to replace
the `## 2026-02-22 - Insecure Default Permissions on Backups` line with `#
2026-02-22 - Insecure Default Permissions on Backups`, add a blank line before
and after that heading, and break the subsequent sentence lines (the
Vulnerability, Learning, and Prevention lines referencing
tools/backup-projects.sh and umask 077) into shorter lines so each stays under
the 80-character limit.

In `@tests/verify_backup_permissions.sh`:
- Around line 9-13: The trap currently unconditionally removes "$PROJECT_DIR" on
EXIT which will delete an existing $HOME/kidchenko; modify the setup to detect
whether PROJECT_DIR already existed (e.g., check [ -d "$PROJECT_DIR" ] before
creating, set a flag like PROJECT_DIR_PREEXISTED or PROJECT_DIR_CREATED), only
create the directory if missing (mkdir -p as now) and update the trap to remove
"$PROJECT_DIR" on EXIT only when the flag indicates the test created it; use
PROJECT_DIR and the trap command references to locate where to add the existence
check and conditional cleanup.
- Around line 1-2: Add strict-mode to the shell script to prevent silent
failures: insert "set -euo pipefail" after the shebang so that failures (e.g.,
stat errors or unset variables like PERMS) cause the script to exit with clear
errors instead of producing empty/wrong PERMS or misleading test results; ensure
any use of PERMS and other variables are handled with proper quoting to satisfy
-u.

---

Nitpick comments:
In `@tests/verify_backup_permissions.sh`:
- Line 19: Replace the hard-coded /tmp/backup_output.log with a secure temporary
file created via mktemp and store its path in a BACKUP_LOG variable, ensure the
file is created with restrictive permissions (or set a safe umask before
creation), use "$BACKUP_LOG" when invoking bash tools/backup-projects.sh, and
add "$BACKUP_LOG" to the existing cleanup function invoked by the trap so the
temp file is removed on exit/failure (refer to BACKUP_LOG, the cleanup function,
and the trap setup to locate where to modify).

Comment on lines +1 to +4
## 2026-02-22 - Insecure Default Permissions on Backups
**Vulnerability:** The `tools/backup-projects.sh` script created zip archives with default permissions (often `644` or `664`), allowing other users on the system to read potentially sensitive project backups.
**Learning:** Shell scripts using tools like `zip` or `tar` do not automatically restrict permissions of the output file unless `umask` is set.
**Prevention:** Always set `umask 077` at the beginning of shell scripts that generate sensitive files or directories to ensure they are private by default.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor

Fix markdown lint failures blocking the CI "Lint Documentation" check.

Five violations are reported:

  • MD041 (line 1): First line must be a top-level # heading, not ##.
  • MD022 (line 1): Heading must be surrounded by blank lines.
  • MD013 (lines 2–4): Lines exceed the 80-character limit.
πŸ“ Proposed fix
-## 2026-02-22 - Insecure Default Permissions on Backups
-**Vulnerability:** The `tools/backup-projects.sh` script created zip archives with default permissions (often `644` or `664`), allowing other users on the system to read potentially sensitive project backups.
-**Learning:** Shell scripts using tools like `zip` or `tar` do not automatically restrict permissions of the output file unless `umask` is set.
-**Prevention:** Always set `umask 077` at the beginning of shell scripts that generate sensitive files or directories to ensure they are private by default.
+# Security Bulletins
+
+## 2026-02-22 - Insecure Default Permissions on Backups
+
+**Vulnerability:** `tools/backup-projects.sh` created zip archives with default
+permissions (often `644` or `664`), allowing other users to read sensitive
+project backups.
+
+**Learning:** Shell scripts using `zip` or `tar` do not restrict output file
+permissions unless `umask` is set.
+
+**Prevention:** Set `umask 077` at the start of scripts that generate sensitive
+files or directories to ensure owner-only permissions by default.
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## 2026-02-22 - Insecure Default Permissions on Backups
**Vulnerability:** The `tools/backup-projects.sh` script created zip archives with default permissions (often `644` or `664`), allowing other users on the system to read potentially sensitive project backups.
**Learning:** Shell scripts using tools like `zip` or `tar` do not automatically restrict permissions of the output file unless `umask` is set.
**Prevention:** Always set `umask 077` at the beginning of shell scripts that generate sensitive files or directories to ensure they are private by default.
# Security Bulletins
## 2026-02-22 - Insecure Default Permissions on Backups
**Vulnerability:** `tools/backup-projects.sh` created zip archives with default
permissions (often `644` or `664`), allowing other users to read sensitive
project backups.
**Learning:** Shell scripts using `zip` or `tar` do not restrict output file
permissions unless `umask` is set.
**Prevention:** Set `umask 077` at the start of scripts that generate sensitive
files or directories to ensure owner-only permissions by default.
🧰 Tools
πŸͺ› GitHub Check: Lint Documentation

[failure] 4-4: Line length
.jules/sentinel.md:4:81 MD013/line-length Line length [Expected: 80; Actual: 156] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md013.md


[failure] 3-3: Line length
.jules/sentinel.md:3:81 MD013/line-length Line length [Expected: 80; Actual: 143] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md013.md


[failure] 2-2: Line length
.jules/sentinel.md:2:81 MD013/line-length Line length [Expected: 80; Actual: 208] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md013.md


[failure] 1-1: First line in a file should be a top-level heading
.jules/sentinel.md:1 MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "## 2026-02-22 - Insecure Defau..."] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md041.md


[failure] 1-1: Headings should be surrounded by blank lines
.jules/sentinel.md:1 MD022/blanks-around-headings Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] [Context: "## 2026-02-22 - Insecure Default Permissions on Backups"] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.jules/sentinel.md around lines 1 - 4, Change the top-level heading to a
single `#` and ensure it's surrounded by a blank line, and wrap the description
lines to <=80 characters to satisfy MD041, MD022 and MD013; specifically edit
.jules/sentinel.md to replace the `## 2026-02-22 - Insecure Default Permissions
on Backups` line with `# 2026-02-22 - Insecure Default Permissions on Backups`,
add a blank line before and after that heading, and break the subsequent
sentence lines (the Vulnerability, Learning, and Prevention lines referencing
tools/backup-projects.sh and umask 077) into shorter lines so each stays under
the 80-character limit.

Comment on lines +1 to +2
#!/bin/bash

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add set -euo pipefail to prevent silent failures from producing misleading test results.

Without strict mode, a failing intermediate command (e.g., stat encountering a missing file, or an unset variable expanding to empty) leaves $PERMS empty or wrong, causing the final comparison to report a false failure β€” or worse, a false pass β€” with no indication of what went wrong.

♻️ Proposed fix
 #!/bin/bash
+set -euo pipefail
+
 # Setup test environment
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/verify_backup_permissions.sh` around lines 1 - 2, Add strict-mode to
the shell script to prevent silent failures: insert "set -euo pipefail" after
the shebang so that failures (e.g., stat errors or unset variables like PERMS)
cause the script to exit with clear errors instead of producing empty/wrong
PERMS or misleading test results; ensure any use of PERMS and other variables
are handled with proper quoting to satisfy -u.

Comment on lines +9 to +13
mkdir -p "$PROJECT_DIR"
echo "secret content" > "$PROJECT_DIR/secret.txt"

# Ensure cleanup
trap 'rm -rf "$PROJECT_DIR"' EXIT
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | πŸ”΄ Critical

trap 'rm -rf "$PROJECT_DIR"' EXIT will unconditionally destroy ~/kidchenko if it exists with real data.

PROJECT_DIR is set to $HOME/kidchenko β€” a directory that likely contains the owner's real projects. mkdir -p silently succeeds when the directory already exists, so the trap is armed and fires on every exit, deleting everything. Any developer running this test locally on their dotfiles machine loses all their project data.

Fix: check whether the directory pre-existed and only delete it if this test created it.

πŸ›‘οΈ Proposed fix
-# Create dummy project
-mkdir -p "$PROJECT_DIR"
-echo "secret content" > "$PROJECT_DIR/secret.txt"
-
-# Ensure cleanup
-trap 'rm -rf "$PROJECT_DIR"' EXIT
+# Create dummy project – track pre-existence to avoid destroying real data
+_project_dir_existed=false
+[[ -d "$PROJECT_DIR" ]] && _project_dir_existed=true
+mkdir -p "$PROJECT_DIR"
+echo "secret content" > "$PROJECT_DIR/secret.txt"
+
+# Cleanup: only remove the directory wholesale if this test created it
+_cleanup() {
+  rm -f "$PROJECT_DIR/secret.txt"
+  [[ "$_project_dir_existed" == false ]] && rm -rf "$PROJECT_DIR"
+}
+trap _cleanup EXIT
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
mkdir -p "$PROJECT_DIR"
echo "secret content" > "$PROJECT_DIR/secret.txt"
# Ensure cleanup
trap 'rm -rf "$PROJECT_DIR"' EXIT
# Create dummy project – track pre-existence to avoid destroying real data
_project_dir_existed=false
[[ -d "$PROJECT_DIR" ]] && _project_dir_existed=true
mkdir -p "$PROJECT_DIR"
echo "secret content" > "$PROJECT_DIR/secret.txt"
# Cleanup: only remove the directory wholesale if this test created it
_cleanup() {
rm -f "$PROJECT_DIR/secret.txt"
[[ "$_project_dir_existed" == false ]] && rm -rf "$PROJECT_DIR"
}
trap _cleanup EXIT
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/verify_backup_permissions.sh` around lines 9 - 13, The trap currently
unconditionally removes "$PROJECT_DIR" on EXIT which will delete an existing
$HOME/kidchenko; modify the setup to detect whether PROJECT_DIR already existed
(e.g., check [ -d "$PROJECT_DIR" ] before creating, set a flag like
PROJECT_DIR_PREEXISTED or PROJECT_DIR_CREATED), only create the directory if
missing (mkdir -p as now) and update the trap to remove "$PROJECT_DIR" on EXIT
only when the flag indicates the test created it; use PROJECT_DIR and the trap
command references to locate where to add the existence check and conditional
cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant